import { useEffect, useRef, useState } from 'react'
import styles from './ProgressBar.module.scss'
function standardization(value: number) {
  return Math.max(0, Math.min(1, value))
}

function getProgressValue(min: number, max: number, progress: number) {
  return min + standardization(progress) * (max - min)
}
export default function ProgressBar(props: {
  progress: number
  onChange?: (progress: number, isDrag: boolean) => void
  className?: string
  style?: React.CSSProperties
  min?: number
  max?: number
}) {
  const min = props.min ?? 0
  const max = props.max ?? 1
  const [lastProgress, setLastProgress] = useState(0)
  const [isDragState, setIsDrag] = useState(false)
  const isDrag = useRef(false)
  const dragStartX = useRef(0)
  const [offsetProgress, setOffsetProgress] = useState(0)
  const containerRef = useRef<HTMLDivElement>(null)
  const containerWidth = useRef(100)
  function onMouseDown(e: React.MouseEvent) {
    e.stopPropagation()
    containerWidth.current = containerRef.current!.getBoundingClientRect().width
    dragStartX.current = e.clientX
    setOffsetProgress(0)
    setLastProgress(props.progress)
    setIsDrag(true)
  }
  function onMouseMove(e: MouseEvent) {
    e.stopPropagation()
    if (isDrag.current) {
      const offsetX = e.clientX - dragStartX.current
      let offsetProgress = offsetX / containerWidth.current
      offsetProgress = Math.max(-1, Math.min(1, offsetProgress))
      setOffsetProgress(offsetProgress)
      let progress = lastProgress + offsetProgress
      progress = Math.max(0, Math.min(1, progress))
      props.onChange?.(getProgressValue(min, max, progress), true)
    }
  }
  function onMouseUp(e: MouseEvent) {
    e.stopPropagation()
    if (isDrag.current) {
      const offsetX = e.clientX - dragStartX.current
      let progress = lastProgress + offsetX / containerWidth.current
      progress = Math.max(0, Math.min(1, progress))
      props.onChange?.(getProgressValue(min, max, progress), false)
      setIsDrag(false)
    }
  }
  function onClick(e: React.MouseEvent) {
    const progress = e.nativeEvent.layerX / e.currentTarget.clientWidth
    props.onChange?.(getProgressValue(min, max, progress), false)
  }

  useEffect(() => {
    isDrag.current = isDragState
    if (isDragState) {
      window.addEventListener('mousemove', onMouseMove)
      window.addEventListener('mouseup', onMouseUp)
      return () => {
        window.removeEventListener('mousemove', onMouseMove)
        window.removeEventListener('mouseup', onMouseUp)
      }
    }
  }, [isDragState])
  useEffect(() => {
    const rect = containerRef.current?.getBoundingClientRect()
    if (rect) containerWidth.current = rect.width
  }, [])

  function getWidth() {
    if (!isDragState) {
      return 100 * props.progress
    }
    return Math.max(0, Math.min(1, lastProgress + offsetProgress)) * 100
  }

  return (
    <div
      className={
        styles['progress-bar'] + (props.className ? ` ${props.className}` : '')
      }
      style={props.style}
      ref={containerRef}
      onClick={onClick}>
      <div
        className={styles['progress-bar-inner']}
        style={{
          width: `${getWidth()}%`,
          transition: isDragState ? '0s' : 'width 0.3s',
        }}></div>
      <div
        onClick={(e) => e.stopPropagation()}
        onMouseDown={onMouseDown}
        className={styles['progress-bar-inner-box']}
        style={{
          left: `${getWidth()}%`,
          transition: isDragState ? '0s' : '0.3s',
        }}></div>
    </div>
  )
}
